using UnityEngine;

public class Archer : Enemy
{
    public ArcherGroundState GroundState { get; private set; }
    public ArcherIdleState IdleState { get; private set; }
    public ArcherMoveState MoveState { get; private set; }
    public ArcherBattleState BattleState { get; private set; }
    public ArcherAttackState AttackState { get; private set; }
    public ArcherStunnedState StunnedState { get; private set; }
    public ArcherDeadState DeadState { get; private set; }
    public ArcherJumpState JumpState { get; private set; }
    public ArcherCloseAttackState CloseAttackState { get; private set; }

    [Header("Archer spisifc info")]
    [SerializeField] public Vector2 JumpVelocity;
    private GameObject arrowPrefab;
    public float JumpCooldown;
    public float safeDistance; // how close player should be to trigger jump to battle state
    [HideInInspector] public float LastTimeJumped;
    [Header("Arrow info")]
    [SerializeField] private float arrowSpeed = 1;
    [SerializeField] private int arrowDamage = 1;
    [Header("Additional collision check")]
    [SerializeField] private Vector2 groundBehindCheckSize = new(1, 1);
    private Transform groundBehindCheck;
    [SerializeField] private float wallBehindCheckDistance = 2;

    protected override void Awake()
    {
        base.Awake();
        GroundState = new(this, "idle");
        IdleState = new(this, "idle");
        MoveState = new(this, "move");
        BattleState = new(this, "move");
        AttackState = new(this, "attack");
        StunnedState = new(this, "stunned");
        DeadState = new(this, "dead");
        JumpState = new(this, "jump");
        CloseAttackState = new(this, "move");
        arrowPrefab = Resources.Load<GameObject>("Prefabs/ArrowController");
        groundBehindCheck = transform.Find("GroundBehindCheck");
    }

    private void Start()
    {
        StateMachine.Initialize(IdleState);
    }

    public override bool CanBeStunned()
    {
        if (canBeStunned)
        {
            StateMachine.ChangeTo(StunnedState);
            return true;
        }
        return false;
    }

    public override void Die()
    {
        StateMachine.ChangeTo(DeadState);
    }

    public override void SpeicalAttackTrigger()
    {
        GameObject obj = Instantiate(arrowPrefab, AttackPoint.position, Quaternion.identity);
        obj.GetComponent<ArrowController>().Setup(Stats, Player.Stats, FacingDirection, arrowSpeed, arrowDamage);
    }

    public bool GroundBehindCheck() => Physics2D.BoxCast(groundBehindCheck.position, groundBehindCheckSize, 0, Vector2.zero, groundLayer);
    public bool WallBehindCheck() => Physics2D.Linecast(transform.position, transform.position + new Vector3(wallBehindCheckDistance * -FacingDirection, 0, 0), groundLayer);

#if UNITY_EDITOR
    [Header("Settings")]
    [SerializeField] private int resolution = 20; // 轨迹点的数量
    [SerializeField] private float predictionTime = 2f; // 预测时间

    [Header("Gizmos")]
    [SerializeField] private bool showLandingPoint = true;
    [SerializeField] private bool showTrajectory = true;
    [SerializeField] private Color trajectoryColor = Color.green;
    [SerializeField] private Color landingPointColor = Color.red;
    [SerializeField] private float landingPointRadius = 0.2f;

    private Vector2 gravity;

    protected override void OnValidate()
    {
        base.OnValidate();
        groundBehindCheck = transform.Find("GroundBehindCheck");

        Rb = GetComponent<Rigidbody2D>();
        Collider = GetComponent<CapsuleCollider2D>();
        gravity = Physics2D.gravity * Rb.gravityScale;
        // UnityEditor.EditorApplication.delayCall += () => UnityEditor.EditorUtility.SetDirty(this);
    }

    protected void OnDrawGizmosSelected()
    {
        Gizmos.DrawCube(groundBehindCheck.position, groundBehindCheckSize);

        Gizmos.DrawLine(transform.position, transform.position + new Vector3(wallBehindCheckDistance * -FacingDirection, 0, 0));

        Vector2 startPosition = (Vector2)transform.position;
        Vector2 velocity = JumpVelocity;
        velocity.x *= -FacingDirection;

        Vector2 predictedLandingPoint = CalculateLandingPoint(startPosition, velocity);

        if (showTrajectory)
        {
            DrawTrajectory(startPosition, velocity);
        }

        if (showLandingPoint)
        {
            DrawLandingPoint(predictedLandingPoint);
        }
    }

    private Vector2 CalculateLandingPoint(Vector2 startPos, Vector2 startVelocity)
    {
        // 计算落地的y位置（假设地面在y=0）
        // 使用运动学方程: y = y0 + vy*t + 0.5*a*t^2
        // 解这个二次方程求t
        float a = gravity.y;
        float b = startVelocity.y;
        float c = startPos.y;
        float y = transform.position.y - Collider.size.y * 0.5f;

        // 解二次方程求时间
        float discriminant = b * b - 4 * a * c;

        if (discriminant < 0)
        {
            // 不会落地的情况
            return startPos + startVelocity * predictionTime + 0.5f * predictionTime * predictionTime * gravity;
        }

        float sqrtDiscriminant = Mathf.Sqrt(discriminant);
        float t1 = (-b + sqrtDiscriminant) / (2 * a);
        float t2 = (-b - sqrtDiscriminant) / (2 * a);

        float time = Mathf.Max(t1, t2); // 取正值

        // 计算x位置
        float x = startPos.x + startVelocity.x * time;

        return new Vector2(x, y); // 假设地面在y=0
    }

    private void DrawTrajectory(Vector2 startPos, Vector2 startVelocity)
    {
        Gizmos.color = trajectoryColor;

        Vector2 prevPoint = startPos;

        for (int i = 1; i <= resolution; i++)
        {
            float t = i * (predictionTime / resolution);
            Vector2 point = startPos + startVelocity * t + 0.5f * gravity * t * t;

            Gizmos.DrawLine(prevPoint, point);
            prevPoint = point;
        }
    }

    private void DrawLandingPoint(Vector2 point)
    {
        Gizmos.color = landingPointColor;
        Gizmos.DrawWireSphere(point, landingPointRadius);

        // 绘制从最后一个轨迹点到落点的线
        Vector2 lastTrajectoryPoint = (Vector2)transform.position + JumpVelocity * FacingDirection * predictionTime +
                                     0.5f * gravity * predictionTime * predictionTime;
        Gizmos.DrawLine(lastTrajectoryPoint, point);
    }

    public void SetInitialVelocity(Vector2 velocity)
    {
        JumpVelocity = velocity;
    }
#endif
}
